#!/usr/bin/env python3

from bottle import get,post,request,Bottle,run,template
import threading,time,json,zlib,gnupg,socket,psutil,os,sys,requests,uuid
from queue import Queue

payload_haus=["wasser","gas","kwh_haus","kwh_herd"]
payload_kfz=["km","volumen","preis"]

macadress=str(hex(uuid.getnode()))[2:-1]
pathname = os.path.dirname(sys.argv[0])        
abspath=os.path.abspath(pathname)
configfile=abspath+"/config.json"
try:
	cf=open(configfile,"r")
except:
	cf=open(configfile+".template","r")

log_conf=json.load(cf)
cf.close()

sensors={
"bme280":{"enable":0,"script":"log_bme280.py","running":0},
"tristar":{"enable":0,"script":"log_tristar.py","running":0},
"vedirect":{"enable":0,"script":"log_vedirect.py","running":0},
"mcp9808": {"enable":0,"script":"log_mcp9808.py","running":0},
"tsl2591":{"enable":0,"script":"log_tsl2591.py","running":0},
"veml6070":{"enable":0,"script":"log_veml6070.py","running":0},
"w1therm":{"enable":0,"script":"log_w1.py","running":0},
"ads1x15":{"enable":0,"script":"log_ads1x15.py","running":0}
}

parameter={"device":socket.gethostname(),"allowed_ip":{"127.0.0.1":"25A4CF79414F10FD"},"gpg_keyid":"25A4CF79414F10FD","server_transfer_wait":2,"server_keyid":"25A4CF79414F10FD"}
for n in parameter:
  if n in log_conf:
    parameter[n]=log_conf[n]

for sns in sensors:
	if sns in log_conf:
		if 'enable' in log_conf[sns]:
			if log_conf[sns]['enable'] == 1:
				sensors[sns]['enable']=1

if "sqlserver" in log_conf:
  hostname="localhost"
  if "host" in log_conf['sqlserver']:
	  hostname=log_conf['sqlserver']['host']
  port=8081
#  if "port" in log_conf['sqlserver']:
#	  port=int(log_conf['sqlserver']['port'])


gpg=gnupg.GPG('/usr/bin/gpg')
try:
	gpgkey=gpg.list_keys(keys=parameter['gpg_keyid'])[0]
except:
	gpgkey=[]

try:
	servergpgkey=gpg.list_keys(keys=parameter['server_keyid'])[0]
except:
	servergpgkey=[]

measdata={}
count_tickets=0
last_ticket_time=0

_HASH="hash"
_SIGNEDGPG="signed_gpg"
_PAYLOAD="payload"
_MEASURES="measures"
_BEGINSIGNATURE="-----BEGIN PGP SIGNATURE-----"
_BEGINMESSAGE="-----BEGIN PGP SIGNED MESSAGE-----"
_BEGINHASH="Hash:"

def sql_insert(q):
	count_tickets=0
	measdata={}
	server_last_transmit=0
	while True:
		if q.empty():
			time.sleep(0.1)
#			print("ping"+str(time.time()))
			if (time.time()-server_last_transmit)>parameter['server_transfer_wait']:
				if len(measdata)==0:
					server_last_transmit=time.time()-parameter['server_transfer_wait']/2
				else:
					json_out={"data":measdata}
					if ('fingerprint' in servergpgkey) and ('fingerprint' in gpgkey):
						json_out={"encrypted_data":gpg.encrypt(json.dumps(measdata),servergpgkey['fingerprint'],sign=gpgkey['fingerprint']).data.decode("utf-8")}
					if ('fingerprint' not in servergpgkey) and ('fingerprint' in gpgkey):
						json_out={"signed_data":gpg.sign(json.dumps(measdata),keyid=gpgkey['keyid']).data.decode("utf-8")}
					print("http://"+hostname+":"+str(port)+"/data/"+gpgkey['keyid'])
					try:
						_r=requests.post("http://"+hostname+":"+str(port)+"/data/"+gpgkey['keyid'],json=json.dumps(json_out))
					except:
						_r={"status_code":404}
						print("could not send to server")
#						print(json.dumps(json_out))
						server_last_transmit=time.time()-parameter['server_transfer_wait']/2
					else:
						if _r.status_code==200:
							measdata={}
							server_last_transmit=time.time()
							print(str(count_tickets)+" tickets send")
							count_tickets=0
						else:
							print("could not send to server")
							print(_r.status_code)
							server_last_transmit=time.time()-parameter['server_transfer_wait']/2
		else:
			try:
				indata=q.get()
				if indata is not None:
					q.task_done()
			except Exception as e:
				print("Error during queuing")
				print(e)
			else:
				count_tickets=count_tickets+1
				if indata['hash'] in measdata:
					for i in indata['payload']['measures']:
						measdata[indata['hash']]['measures'][i]=indata['payload']['measures'][i]
				else:
					measdata[indata['hash']]=indata['payload']

app=Bottle()

@app.get('/')
def approot():
	sensor_list=[]
	for p in psutil.process_iter():
		if p.name() == 'python3':
			sensor_list.append(p.cmdline()[1].split('/')[-1])
	for sns in sensors:
		print(sns)
		if sensors[sns]['enable']==1:
			print(sensors[sns]['script'])
			sensors[sns]['running']= sensors[sns]['script'] in sensor_list
	return template('main.tpl',server=socket.gethostname(),cpupercent=psutil.cpu_percent(),countticket=count_tickets,tickettime=last_ticket_time,measdata=measdata,sensors=sensors)
@app.post('/')
def submit_root():
	sensor_start=request.forms.get('sensor')
	if sensor_start in sensors:
		sensor_script=abspath+'/'+sensors[sensor_start]['script']
		try:
			stored_timestamps=os.path.getsize(sensor_script)
		except:
			print("start script does not exist")
		else:
			os.spawnl(os.P_NOWAIT,sensor_script,' ')

@app.post('/data/<hash_id:int>')
def dataimport(hash_id):
#	print(hash_id)
	timestart=time.time()
	# check if request comes from allowed ip
	if request.remote_addr in parameter['allowed_ip']:
		# check, if json is transmitted
		try:
			json_in=json.loads(request.json)
#			print(json_in)
		except:
			print("no json")
		else:
			if _HASH in json_in:
				if int(json_in[_HASH]) == hash_id:
					bcorrect=False
					if _PAYLOAD in json_in:
						if _MEASURES in json_in[_PAYLOAD]:
							print("unsigned "+str(time.time()-timestart))
							q.put(json_in,block=False)
							bcorrect=True
					if _SIGNEDGPG in json_in:
						# check if signature of data is correct and key is allowed for ip
						vgpg=gpg.verify(json_in[_SIGNEDGPG])
						print(time.time()-timestart)
						if vgpg.valid and vgpg.key_id in parameter['allowed_ip'][request.remote_addr]:
							signed_in=json_in[_SIGNEDGPG].split("\n")
							signed_in[signed_in.index(_BEGINSIGNATURE):]=""
							del signed_in[signed_in.index(_BEGINMESSAGE)]
							del signed_in[signed_in.index("")]
							for h in signed_in:
								if _BEGINHASH in h:
									del signed_in[signed_in.index(h)]
							if len(signed_in)>0:
								last_ticket_time=time.time()-timestart
								print(last_ticket_time)
								q.put(json.loads(signed_in[0]),block=False)
							else:
								print("malformed signed packet")
								print(json_in)
						else:
							print("could not verify gpg signature")
							print(json_in)
				else:
					print("wrong id")
			else:
				print("json has no hash field")
				print(json_in)
	else:
		print("not allowed client address")

@app.get('/haus')
def haus():
		return template('''
			<form action="/haus" method="post">
				epoch: <input name="epoch" type="text" value={{td}} /></br>
				wasser: <input name="wasser" type="text" /></br>
				gas: <input name="gas" type="text" /></br>
				Strom: <input name="kwh_haus" type="text" /></br>
				Strom Herd: <input name="kwh_herd" type="text" /></br>
				<input value="Senden" type="submit" />
			</form>
		''',td=str(int(1000*time.time())))

@app.post('/haus')
def submit_haus():
	indata={}
	json_out={}
	xhash=macadress+'banana'+str(0)+'hand'
	ts=request.forms.get('epoch')
	for x in payload_haus:
		idstring=macadress+'banana'+str(0)+'hand'+x
		xhash=zlib.crc32(idstring.encode("utf-8")) % (1<<32)
		json_out[xhash]={"hash":xhash,"signature":"","payload":{"device":"banana","varname":x,"i2c":0,"sensor":"manuell","mac":macadress,"measures":{ts:request.forms.get(x)}}}
		q.put({"hash":xhash,"signature":"","payload":{"device":"banana","varname":x,"i2c":0,"sensor":"manuell","mac":macadress,"measures":{ts:request.forms.get(x)}}},block=False)
	print(indata)

@app.get('/kfz')
def kfz():
		return template('''
			<form action="/kfz" method="post">
				epoch: <input name="epoch" type="text" value={{td}} /></br>
				km-Stand: <input name="km" type="text" /></br>
				Volumen: <input name="volumen" type="text" /></br>
				Preis: <input name="preis" type="text" /></br>
				<input value="Senden" type="submit" />
			</form>
		''',td=str(int(1000*time.time())))

@app.post('/kfz')
def submit_kfz():
	indata={}
	json_out={}
	xhash=macadress+'banana'+str(0)+'hand'
	ts=request.forms.get('epoch')
	for x in payload_kfz:
		idstring=macadress+'banana'+str(0)+'hand'+x
		xhash=zlib.crc32(idstring.encode("utf-8")) % (1<<32)
		json_out[xhash]={"hash":xhash,"signature":"","payload":{"device":"banana","varname":x,"i2c":0,"sensor":"manuell","mac":macadress,"measures":{ts:request.forms.get(x)}}}
		q.put({"hash":xhash,"signature":"","payload":{"device":"banana","varname":x,"i2c":0,"sensor":"manuell","mac":macadress,"measures":{ts:request.forms.get(x)}}},block=False)
	print(json_out)
#	q.put(json_out,block=False)

@app.get('/test')
def get_test():
		return template('''
			<form action="/test" method="post">
				<input name="but" value="Senden1" type="submit" /></br>
				<input name="but" value="Senden2" type="submit" /></br>
				<input name="but" value="Senden3" type="submit" /></br>
			</form>
		''')

@app.post('/test')
def submit_test():
	print(request.forms.get('but'))

q=Queue(maxsize=0)

sql_worker=threading.Thread(target=sql_insert,args=(q,))
sql_worker.setDaemon(True)
sql_worker.start()


run(app,host="",port=8080)
